/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"


#include "z_zone.h"
#include "doomdef.h"
#include "p_local.h"

#include "s_sound.h"

// State.
#include "doomstat.h"
#include "r_state.h"
// Data.
#include "sounds.h"


//
// FLOORS
//

//
// Move a plane (floor or ceiling) and check for crushing
//
result_e
T_MovePlane
( sector_t*	sector,
  fixed_t	speed,
  fixed_t	dest,
  qboolean	crush,
  int		floorOrCeiling,
  int		direction )
{
	qboolean	flag;
	fixed_t	lastpos;

	switch( floorOrCeiling )
	{
		case 0:
			// FLOOR
			switch( direction )
			{
				case -1:
					// DOWN
					if( sector->floorheight - speed < dest )
					{
						lastpos = sector->floorheight;
						sector->floorheight = dest;
						flag = P_ChangeSector( sector, crush );
						if( flag == true )
						{
							sector->floorheight = lastpos;
							P_ChangeSector( sector, crush );
							//return crushed;
						}
						return pastdest;
					}
					else
					{
						lastpos = sector->floorheight;
						sector->floorheight -= speed;
						flag = P_ChangeSector( sector, crush );
						if( flag == true )
						{
							sector->floorheight = lastpos;
							P_ChangeSector( sector, crush );
							return crushed;
						}
					}
					break;

				case 1:
					// UP
					if( sector->floorheight + speed > dest )
					{
						lastpos = sector->floorheight;
						sector->floorheight = dest;
						flag = P_ChangeSector( sector, crush );
						if( flag == true )
						{
							sector->floorheight = lastpos;
							P_ChangeSector( sector, crush );
							//return crushed;
						}
						return pastdest;
					}
					else
					{
						// COULD GET CRUSHED
						lastpos = sector->floorheight;
						sector->floorheight += speed;
						flag = P_ChangeSector( sector, crush );
						if( flag == true )
						{
							if( crush == true )
							{
								return crushed;
							}
							sector->floorheight = lastpos;
							P_ChangeSector( sector, crush );
							return crushed;
						}
					}
					break;
			}
			break;

		case 1:
			// CEILING
			switch( direction )
			{
				case -1:
					// DOWN
					if( sector->ceilingheight - speed < dest )
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight = dest;
						flag = P_ChangeSector( sector, crush );

						if( flag == true )
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector( sector, crush );
							//return crushed;
						}
						return pastdest;
					}
					else
					{
						// COULD GET CRUSHED
						lastpos = sector->ceilingheight;
						sector->ceilingheight -= speed;
						flag = P_ChangeSector( sector, crush );

						if( flag == true )
						{
							if( crush == true )
							{
								return crushed;
							}
							sector->ceilingheight = lastpos;
							P_ChangeSector( sector, crush );
							return crushed;
						}
					}
					break;

				case 1:
					// UP
					if( sector->ceilingheight + speed > dest )
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight = dest;
						flag = P_ChangeSector( sector, crush );
						if( flag == true )
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector( sector, crush );
							//return crushed;
						}
						return pastdest;
					}
					else
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight += speed;
						flag = P_ChangeSector( sector, crush );
// UNUSED
#if 0
						if( flag == true )
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector( sector, crush );
							return crushed;
						}
#endif
					}
					break;
			}
			break;

	}
	return ok;
}


//
// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
//
void T_MoveFloor( floormove_t* floor )
{
	result_e	res;

	res = T_MovePlane( floor->sector,
					   floor->speed,
					   floor->floordestheight,
					   floor->crush, 0, floor->direction );

	if( !( ::g->leveltime & 7 ) )
		S_StartSound( &floor->sector->soundorg,
					  sfx_stnmov );

	if( res == pastdest )
	{
		floor->sector->specialdata = NULL;

		if( floor->direction == 1 )
		{
			switch( floor->type )
			{
				case donutRaise:
					floor->sector->special = floor->newspecial;
					floor->sector->floorpic = floor->texture;
				default:
					break;
			}
		}
		else if( floor->direction == -1 )
		{
			switch( floor->type )
			{
				case lowerAndChange:
					floor->sector->special = floor->newspecial;
					floor->sector->floorpic = floor->texture;
				default:
					break;
			}
		}
		P_RemoveThinker( &floor->thinker );

		S_StartSound( &floor->sector->soundorg,
					  sfx_pstop );
	}

}

//
// HANDLE FLOOR TYPES
//
int
EV_DoFloor
( line_t*	line,
  floor_e	floortype )
{
	int			secnum;
	int			rtn;
	int			i;
	sector_t*		sec;
	floormove_t*	floor;

	secnum = -1;
	rtn = 0;
	while( ( secnum = P_FindSectorFromLineTag( line, secnum ) ) >= 0 )
	{
		sec = &::g->sectors[secnum];

		// ALREADY MOVING?  IF SO, KEEP GOING...
		if( sec->specialdata )
		{
			continue;
		}

		// new floor thinker
		rtn = 1;
		floor = ( floormove_t* )DoomLib::Z_Malloc( sizeof( *floor ), PU_LEVEL, 0 );
		P_AddThinker( &floor->thinker );
		sec->specialdata = floor;
		floor->thinker.function.acp1 = ( actionf_p1 ) T_MoveFloor;
		floor->type = floortype;
		floor->crush = false;

		switch( floortype )
		{
			case lowerFloor:
				floor->direction = -1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight =
					P_FindHighestFloorSurrounding( sec );
				break;

			case lowerFloorToLowest:
				floor->direction = -1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight =
					P_FindLowestFloorSurrounding( sec );
				break;

			case turboLower:
				floor->direction = -1;
				floor->sector = sec;
				floor->speed = FLOORSPEED * 4;
				floor->floordestheight =
					P_FindHighestFloorSurrounding( sec );
				if( floor->floordestheight != sec->floorheight )
				{
					floor->floordestheight += 8 * FRACUNIT;
				}
				break;

			case raiseFloorCrush:
				floor->crush = true;
			case raiseFloor:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight =
					P_FindLowestCeilingSurrounding( sec );
				if( floor->floordestheight > sec->ceilingheight )
				{
					floor->floordestheight = sec->ceilingheight;
				}
				floor->floordestheight -= ( 8 * FRACUNIT ) *
										  ( floortype == raiseFloorCrush );
				break;

			case raiseFloorTurbo:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED * 4;
				floor->floordestheight =
					P_FindNextHighestFloor( sec, sec->floorheight );
				break;

			case raiseFloorToNearest:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight =
					P_FindNextHighestFloor( sec, sec->floorheight );
				break;

			case raiseFloor24:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight = floor->sector->floorheight +
										 24 * FRACUNIT;
				break;
			case raiseFloor512:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight = floor->sector->floorheight +
										 512 * FRACUNIT;
				break;

			case raiseFloor24AndChange:
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight = floor->sector->floorheight +
										 24 * FRACUNIT;
				sec->floorpic = line->frontsector->floorpic;
				sec->special = line->frontsector->special;
				break;

			case raiseToTexture:
			{
				int	minsize = MAXINT;
				side_t*	side;

				floor->direction = 1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				for( i = 0; i < sec->linecount; i++ )
				{
					if( twoSided( secnum, i ) )
					{
						side = getSide( secnum, i, 0 );
						if( side->bottomtexture >= 0 )
							if( ::g->s_textureheight[side->bottomtexture] <
									minsize )
								minsize =
									::g->s_textureheight[side->bottomtexture];
						side = getSide( secnum, i, 1 );
						if( side->bottomtexture >= 0 )
							if( ::g->s_textureheight[side->bottomtexture] <
									minsize )
								minsize =
									::g->s_textureheight[side->bottomtexture];
					}
				}
				floor->floordestheight =
					floor->sector->floorheight + minsize;
			}
			break;

			case lowerAndChange:
				floor->direction = -1;
				floor->sector = sec;
				floor->speed = FLOORSPEED;
				floor->floordestheight =
					P_FindLowestFloorSurrounding( sec );
				floor->texture = sec->floorpic;

				for( i = 0; i < sec->linecount; i++ )
				{
					if( twoSided( secnum, i ) )
					{
						if( getSide( secnum, i, 0 )->sector -::g->sectors == secnum )
						{
							sec = getSector( secnum, i, 1 );

							if( sec->floorheight == floor->floordestheight )
							{
								floor->texture = sec->floorpic;
								floor->newspecial = sec->special;
								break;
							}
						}
						else
						{
							sec = getSector( secnum, i, 0 );

							if( sec->floorheight == floor->floordestheight )
							{
								floor->texture = sec->floorpic;
								floor->newspecial = sec->special;
								break;
							}
						}
					}
				}
			default:
				break;
		}
	}
	return rtn;
}




//
// BUILD A STAIRCASE!
//
int
EV_BuildStairs
( line_t*	line,
  stair_e	type )
{
	int			secnum;
	int			height;
	int			i;
	int			newsecnum;
	int			texture;
	int			ok;
	int			rtn;

	sector_t*		sec;
	sector_t*		tsec;

	floormove_t*	floor;

	fixed_t		stairsize = 0;
	fixed_t		speed = 0;

	secnum = -1;
	rtn = 0;
	while( ( secnum = P_FindSectorFromLineTag( line, secnum ) ) >= 0 )
	{
		sec = &::g->sectors[secnum];

		// ALREADY MOVING?  IF SO, KEEP GOING...
		if( sec->specialdata )
		{
			continue;
		}

		// new floor thinker
		rtn = 1;
		floor = ( floormove_t* )DoomLib::Z_Malloc( sizeof( *floor ), PU_LEVEL, 0 );
		P_AddThinker( &floor->thinker );
		sec->specialdata = floor;
		floor->thinker.function.acp1 = ( actionf_p1 ) T_MoveFloor;
		floor->direction = 1;
		floor->sector = sec;
		switch( type )
		{
			case build8:
				speed = FLOORSPEED / 4;
				stairsize = 8 * FRACUNIT;
				break;
			case turbo16:
				speed = FLOORSPEED * 4;
				stairsize = 16 * FRACUNIT;
				break;
		}
		floor->speed = speed;
		height = sec->floorheight + stairsize;
		floor->floordestheight = height;

		texture = sec->floorpic;

		// Find next sector to raise
		// 1.	Find 2-sided line with same sector side[0]
		// 2.	Other side is the next sector to raise
		do
		{
			ok = 0;
			for( i = 0; i < sec->linecount; i++ )
			{
				if( !( ( sec->lines[i] )->flags & ML_TWOSIDED ) )
				{
					continue;
				}

				tsec = ( sec->lines[i] )->frontsector;
				newsecnum = tsec -::g->sectors;

				if( secnum != newsecnum )
				{
					continue;
				}

				tsec = ( sec->lines[i] )->backsector;
				newsecnum = tsec - ::g->sectors;

				if( tsec->floorpic != texture )
				{
					continue;
				}

				height += stairsize;

				if( tsec->specialdata )
				{
					continue;
				}

				sec = tsec;
				secnum = newsecnum;
				floor = ( floormove_t* )DoomLib::Z_Malloc( sizeof( *floor ), PU_LEVEL, 0 );

				P_AddThinker( &floor->thinker );

				sec->specialdata = floor;
				floor->thinker.function.acp1 = ( actionf_p1 ) T_MoveFloor;
				floor->direction = 1;
				floor->sector = sec;
				floor->speed = speed;
				floor->floordestheight = height;
				ok = 1;
				break;
			}
		}
		while( ok );
	}
	return rtn;
}


